/*
 * Copyright (c) 2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/opengl/EGLGraphics.h"

#define LOCAL_INVOKE(call_exp, cond, ret_exp)   \
    call_exp;                       \
    if (!(cond)) {                  \
        LOGE("Failed " #call_exp);  \
        ret_exp;                    \
    }

#define LOAD_MESSAGE()  \
    LOGI("Try creating GraphicsDevice from \"%s\".", __FUNC_NAME__)

AL_OGL_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics(void* handle, uint16_t width, uint16_t height)
{
    LOAD_MESSAGE();
    return alkes::EGLGraphics::create((HWND)handle, width, height);
}

#if !defined(ALKES_GLES)

#define USE_SHADER  true
#define CLIENT_VERSION (2)

AL_OGL_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics_shader(void* handle, uint16_t width, uint16_t height)
{
    LOAD_MESSAGE();
    return alkes::EGLGraphics::create((HWND)handle, width, height);
}

AL_OGL_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics_noshader(void* /*handle*/, uint16_t /*width*/, uint16_t /*height*/)
{
    LOAD_MESSAGE();
    LOGE("OpenGL ES 2.0 cannot disable shader feature!");
    return 0;
}

#else

#define USE_SHADER  false
#define CLIENT_VERSION (1)

AL_OGL_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics_shader(void* /*handle*/, uint16_t /*width*/, uint16_t /*height*/)
{
    LOAD_MESSAGE();
    LOGE("OpenGL ES 1.x is not support suader!");
    return 0;
}

AL_OGL_EXPORT_API alkes::IGraphicsDevice* alkes_create_graphics_noshader(void* handle, uint16_t width, uint16_t height)
{
    LOAD_MESSAGE();
    return alkes::EGLGraphics::create((HWND)handle, width, height);
}

#endif

#ifndef USE_SHADER
#define USE_SHADER  true
#endif

namespace alkes {

EGLGraphics* EGLGraphics::create(EGLNativeWindowType native_window, uint16_t width, uint16_t height, TargetBehavior behavior)
{
    EGLBoolean ret;

    // Get default display
    LOCAL_INVOKE(
        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY),
        EGL_NO_DISPLAY != dpy,
        return 0);

    EGLint major, minor;
    LOCAL_INVOKE(
        ret = eglInitialize(dpy, &major, &minor),
        EGL_FALSE != ret,
        return 0);

    LOGI("EGL version %d.%d", major, minor);

    EGLint attrs[] = {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_NONE
    };

    EGLConfig configs[1];
    EGLint num_config;

    LOCAL_INVOKE(
        ret = eglChooseConfig(dpy, attrs, configs, AL_LENGTH(configs), &num_config),
        EGL_FALSE != ret,
        goto error_terminate);

    LOGI("EGL select config #%08x", configs[0]);

    LOCAL_INVOKE(
        EGLSurface surface = eglCreateWindowSurface(dpy, configs[0], native_window, 0),
        EGL_NO_SURFACE != surface,
        goto error_terminate);

    LOCAL_INVOKE(
        ret = eglBindAPI(EGL_OPENGL_ES_API),
        EGL_FALSE != ret,
        goto error_destroy_surface);

    EGLint ctx_attrs[] = {
        EGL_CONTEXT_CLIENT_VERSION, CLIENT_VERSION,
        EGL_NONE
    };
    LOCAL_INVOKE(
        EGLContext ctx = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, ctx_attrs),
        EGL_NO_CONTEXT != ctx,
        goto error_destroy_surface);

    LOCAL_INVOKE(
        ret = eglMakeCurrent(dpy, surface, surface, ctx),
        EGL_FALSE != ret,
        goto error_destroy_context);

    LOGI("EGL initialized! display=0x%08x surface=0x%08x context=0x%08x", dpy, surface, ctx);

    return new EGLGraphics(dpy, ctx, surface, width, height, behavior);

error_destroy_context:
    eglDestroyContext(dpy, ctx);

error_destroy_surface:
    eglDestroySurface(dpy, surface);

error_terminate:
    eglTerminate(dpy);

    return 0;
}

void EGLGraphics::destroy()
{
    destructGLResource();

    delete this;
}

EGLGraphics::EGLGraphics(
    EGLDisplay dpy,
    EGLContext ctx,
    EGLSurface surface,
    uint16_t width,
    uint16_t height,
    TargetBehavior behavior)
: GLGraphics(width, height, behavior, USE_SHADER)
, dpy_(dpy)
, ctx_(ctx)
, surface_(surface)
{
}

EGLGraphics::~EGLGraphics()
{
    EGLBoolean ret;
    LOCAL_INVOKE(ret = eglDestroyContext(dpy_, ctx_), EGL_FALSE != ret, );
    LOCAL_INVOKE(ret = eglDestroySurface(dpy_, surface_), EGL_FALSE != ret, );
    LOCAL_INVOKE(ret = eglTerminate(dpy_), EGL_FALSE != ret, );
}

void EGLGraphics::flip()
{
    EGLBoolean ret;
    LOCAL_INVOKE(ret = eglSwapBuffers(dpy_, surface_), EGL_FALSE != ret, );
}

}
